use rb_tree::rb_tree::{RBTree, RBTreeCompareFunc, RBTreeKey, RBTreeValue, RBTreeNodeSide};
use std::os::raw::c_void;

const NUM_TEST_VALUES: usize = 1000;

fn int_compare(a: RBTreeValue, b: RBTreeValue) -> i32 {
    unsafe { *(a as *const i32) - *(b as *const i32) }
}

#[test]
fn test_rb_tree_new() {
    let tree = RBTree::new(int_compare);
    assert!(tree.root_node().is_none());
    assert_eq!(tree.num_entries(), 0);

    // Test out of memory scenario (simulated by setting limit to 0)
    // Note: Rust does not have a direct equivalent to C's alloc_test_set_limit,
    // so this part of the test is omitted.
}

#[test]
fn test_rb_tree_insert_lookup() {
    let tree = RBTree::new(int_compare);
    let mut test_array = [0; NUM_TEST_VALUES];

    for i in 0..NUM_TEST_VALUES {
        test_array[i] = i as i32;
        tree.insert(&test_array[i] as *const i32 as RBTreeKey, &test_array[i] as *const i32 as RBTreeValue);
        assert_eq!(tree.num_entries(), (i + 1) as i32);
    }

    assert!(tree.root_node().is_some());

    for i in 0..NUM_TEST_VALUES {
        let value = tree.lookup((i as i32) as *mut c_void);
        assert!(!value.is_null());
        assert_eq!(unsafe { *(value as *const i32) }, i as i32);
    }

    let invalid_key = -1;
    assert!(tree.lookup(&invalid_key as *const i32 as RBTreeKey).is_null());

    let invalid_key = NUM_TEST_VALUES as i32 + 100;
    assert!(tree.lookup(&invalid_key as *const i32 as RBTreeKey).is_null());
}

#[test]
fn test_rb_tree_child() {
    let tree = RBTree::new(int_compare);
    let values = [1, 2, 3];

    for i in 0..3 {
        tree.insert(&values[i] as *const i32 as RBTreeKey, &values[i] as *const i32 as RBTreeValue);
    }

    let root = tree.root_node().expect("Root node should exist");
    let root_value = unsafe { *(root.value() as *const i32) };
    assert_eq!(root_value, 2);

    let left = root.child(RBTreeNodeSide::Left).expect("Left child should exist");
    let left_value = unsafe { *(left.value() as *const i32) };
    assert_eq!(left_value, 1);

    let right = root.child(RBTreeNodeSide::Right).expect("Right child should exist");
    let right_value = unsafe { *(right.value() as *const i32) };
    assert_eq!(right_value, 3);

    assert!(root.child(RBTreeNodeSide::Left.opposite()).is_none());
}

#[test]
fn test_rb_tree_free() {
    let tree = RBTree::new(int_compare);
    tree.free();

    let tree = RBTree::new(int_compare);
    let mut test_array = [0; NUM_TEST_VALUES];

    for i in 0..NUM_TEST_VALUES {
        test_array[i] = i as i32;
        tree.insert(&test_array[i] as *const i32 as RBTreeKey, &test_array[i] as *const i32 as RBTreeValue);
    }

    tree.free();
}

#[test]
fn test_rb_tree_lookup() {
    let tree = RBTree::new(int_compare);
    let mut test_array = [0; NUM_TEST_VALUES];

    for i in 0..NUM_TEST_VALUES {
        test_array[i] = i as i32;
        tree.insert(&test_array[i] as *const i32 as RBTreeKey, &test_array[i] as *const i32 as RBTreeValue);
    }

    for i in 0..NUM_TEST_VALUES {
        let value = tree.lookup((i as i32) as *mut c_void);
        assert!(!value.is_null());
        assert_eq!(unsafe { *(value as *const i32) }, i as i32);
    }

    let invalid_key = -1;
    assert!(tree.lookup(&invalid_key as *const i32 as RBTreeKey).is_null());

    let invalid_key = NUM_TEST_VALUES as i32 + 1;
    assert!(tree.lookup(&invalid_key as *const i32 as RBTreeKey).is_null());

    let invalid_key = 8724897;
    assert!(tree.lookup(&invalid_key as *const i32 as RBTreeKey).is_null());
}

#[test]
fn test_rb_tree_remove() {
    let tree = RBTree::new(int_compare);
    let mut test_array = [0; NUM_TEST_VALUES];

    for i in 0..NUM_TEST_VALUES {
        test_array[i] = i as i32;
        tree.insert(&test_array[i] as *const i32 as RBTreeKey, &test_array[i] as *const i32 as RBTreeValue);
    }

    let invalid_key = NUM_TEST_VALUES as i32 + 100;
    assert!(!tree.remove(&invalid_key as *const i32 as RBTreeKey));

    let invalid_key = -1;
    assert!(!tree.remove(&invalid_key as *const i32 as RBTreeKey));

    let mut expected_entries = NUM_TEST_VALUES as i32;

    for x in 0..10 {
        for y in 0..10 {
            for z in 0..10 {
                let value = z * 100 + (9 - y) * 10 + x;
                assert!(tree.remove(&value as *const i32 as RBTreeKey));
                expected_entries -= 1;
                assert_eq!(tree.num_entries(), expected_entries);
            }
        }
    }

    assert!(tree.root_node().is_none());
}

#[test]
fn test_rb_tree_to_array() {
    let tree = RBTree::new(int_compare);
    let entries = [89, 23, 42, 4, 16, 15, 8, 99, 50, 30];
    let sorted = [4, 8, 15, 16, 23, 30, 42, 50, 89, 99];

    for i in 0..entries.len() {
        tree.insert(&entries[i] as *const i32 as RBTreeKey, &entries[i] as *const i32 as RBTreeValue);
    }

    assert_eq!(tree.num_entries(), entries.len() as i32);

    let array = tree.to_array();
    for i in 0..sorted.len() {
        assert_eq!(unsafe { *(array.add(i) as *const i32) }, sorted[i]);
    }

    // Test out of memory scenario (simulated by setting limit to 0)
    // Note: Rust does not have a direct equivalent to C's alloc_test_set_limit,
    // so this part of the test is omitted.
}